/*******************************************************************************
 * Copyright 2010 DemandSpot
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/
package com.demandspot.client.jsonp;

import java.util.HashMap;
import java.util.Map;

import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.dom.client.Element;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.RootPanel;

public class JSONPUtil {

	private static final int TIMEOUT = 10000;
	private static Map<String, Element> callbacksOnDOM = new HashMap<String, Element>();
	private static Map<String, Timer> timers = new HashMap<String, Timer>();
	private static int callbackIndex = 0;
	
    private JSONPUtil() {
	}
    
	public static void executeQuery(String url, JSONPCallback callback) {
		try {
			addScript(url, callback);
		} catch (Throwable cause) {
			callback.onFailure(cause);
		}
	}
	
	private static void addScript(String url, final JSONPCallback callback) {
		String callbackName = "__gwt_callback" + callbackIndex++;		
        Element el = DOM.createElement("script");
        el.setAttribute("type", "text/javascript");
        el.setAttribute("src", url + "&callback=" + callbackName);
        RootPanel.get().getElement().appendChild(el);
        callbacksOnDOM.put(callbackName, el);
        Timer timer = new Timer() {
			public void run() {
				timers.remove(callback);
				cancel();							
				callback.onFailure(new JSONPTimeoutException("Request timed out. Please try again."));
			}        	
        };
        timers.put(callbackName, timer);
	    // Generate a function in the page with the known name we just created
        setup(callbackName, callback);
        timer.schedule(TIMEOUT);
    }	
	
	//used by JSNI code
	private static void removeScript(String callback) {
		Timer timer = timers.get(callback);
		if (timer != null) {					
			timers.remove(callback);
			timer.cancel();
			timer = null;
		}
		
		Element el = callbacksOnDOM.get(callback);
		if (el != null) {
			RootPanel.get().getElement().removeChild(el);
			callbacksOnDOM.remove(callback);
		}				
	}
	
	
	public native static void setup(String callbackName, JSONPCallback callback) /*-{
	  $wnd[callbackName] = function(data) {	  	
	  	@com.dbroker.client.JSONPUtil::removeScript(Ljava/lang/String;)(callbackName);
	    @com.dbroker.client.JSONPUtil::onResult(Lcom/google/gwt/core/client/JavaScriptObject;Lcom/dbroker/client/JSONPCallback;)(data,callback);	    	    
	  }
	}-*/;
	
	public static void onResult(JavaScriptObject jso, JSONPCallback callback) {
		JSONObject json = new JSONObject(jso);
		callback.onSuccess(json);
	}
	
}
